/*****************************************************************************
 * m5311.c
 *
 * Copyright (C) 2019 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"

/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE    "dev.m5311"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define DEV_M5311_UART         PLUM_HAL_UART_ID(1)
#define DEV_M5311_UART_TIMEOUT 1000
#define DEV_M5311_UART_TX_MAX  1024
#define DEV_M5311_UART_RX_MAX  1024
#define DEV_MODULE_NAME        "M5311"

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef enum {
    M5311_MQTT_STAT_UNINITIALED,
    M5311_MQTT_STAT_INITIALED,
    M5311_MQTT_STAT_DISCONNECTED,
    M5311_MQTT_STAT_CONNECTING,
    M5311_MQTT_STAT_RECONNECTING,
    M5311_MQTT_STAT_CONNECTED,
    M5311_MQTT_STAT_TCP_CONNECTING,
    M5311_MQTT_STAT_TCP_CONNECTED,
} m5311_mqtt_stat_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
plum_void m5311_power_on(plum_void)
{
}

PLUM_PRIVATE
plum_s32 m5311_power_off(plum_void)
{
}

PLUM_PRIVATE
plum_s32 m5311_reset(plum_void)
{
}

PLUM_PRIVATE
plum_s32 m5311_iccid(plum_u8 *iccid)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        char cmd[] = "AT+ICCID\r\n";
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            char *ptr = strstr(act, "+ICCID:");
            if (ptr) {
                sscanf(ptr, "+ICCID:%s", iccid);
                rc = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_csq(plum_u8 *rssi, plum_u8 *ber)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        char cmd[] = "AT+CSQ\r\n";
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            char *ptr = strstr(act, "+CSQ");
            if (ptr) {
                int a, b;
                sscanf(ptr, "+CSQ:%d,%d", &a, &b);

                *rssi = a;
                *ber  = b;
                rc    = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_cfg(plum_s8 *client_id, plum_s8 *url, plum_u32 port,
                        plum_s8 *usr, plum_s8 *psswd)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        char cmd[DEV_M5311_UART_TX_MAX] = {0};
        snprintf(cmd, sizeof(cmd),
                 "AT+MQTTCFG=\"%s\",%d,\"%s\",,\"%s\",\"%s\",\r\n", url, port,
                 client_id, usr, psswd);
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            if (strstr(act, "OK") != NULL) {
                rc = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_open(plum_void)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        char cmd[DEV_M5311_UART_TX_MAX] = {0};
        snprintf(cmd, sizeof(cmd), "AT+MQTTOPEN=1,1,0,0,0,\"\",\"\"\r\n");
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            if (strstr(act, "OK") != NULL) {
                rc = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_state(m5311_mqtt_stat_t *sta)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        char cmd[] = "AT+MQTTSTAT?\r\n";
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            char *ptr = strstr(act, "+MQTTSTAT:");
            if (ptr) {
                int n;
                sscanf(ptr, "+MQTTSTAT: %d", &n);
                *sta = (m5311_mqtt_stat_t)n;
                rc   = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_sub(char *topic)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!topic) {
            LOG_E("topic is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        char cmd[DEV_M5311_UART_TX_MAX] = {0};
        snprintf(cmd, sizeof(cmd), "AT+MQTTSUB=\"%s\",1\r\n", topic);
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            if (strstr(act, "OK")) {
                rc = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_pub(plum_s8 *topic, plum_s8 *msg, plum_u32 size)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!topic) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if (!msg) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        char cmd[DEV_M5311_UART_TX_MAX] = {0};
        snprintf(cmd, sizeof(cmd), "AT+MQTTPUB=\"%s\",1,0,0,%d,\"%s\"\r\n",
                 topic, size, msg);
        LOG_D("%s TX: %s", DEV_MODULE_NAME, cmd);
        rc = plum_hal_uart_send(DEV_M5311_UART, cmd, strlen(cmd));
        if (rc < 0) {
            LOG_E("plum_hal_uart_send err,rc:%d", rc);
            break;
        }

        char act[DEV_M5311_UART_RX_MAX] = {0};
        rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
        LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
        if (rc > 0) {
            if (strstr(act, "OK")) {
                rc = PLUM_ECODE_OK;
            }
            else {
                rc = PLUM_ECODE_INVALID;
            }
        }
        else {
            rc = PLUM_ECODE_TIMEOUT;
        }
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 m5311_mqtt_connect(plum_s8 *client_id, plum_s8 *url, plum_u32 port,
                            plum_s8 *usr, plum_s8 *passwd)
{
    plum_s32 rc = PLUM_ECODE_OK;

    m5311_mqtt_stat_t state;
    plum_bit          exit = plum_false;

    while (!exit) {
        rc = m5311_mqtt_state(&state);
        if (rc) {
            LOG_E("m5311_mqtt_state err,rc:%d", rc);
            continue;
        }

        switch (state) {
            case M5311_MQTT_STAT_UNINITIALED:
                /* steps1: */
                rc = m5311_mqtt_cfg(client_id, url, port, usr, passwd);
                if (rc) {
                    LOG_E("m5311_mqtt_cfg error,rc:%d\r\n", rc);
                }
                break;

            case M5311_MQTT_STAT_INITIALED:
            case M5311_MQTT_STAT_DISCONNECTED:
                /* steps2: */
                rc = m5311_mqtt_open();
                if (rc) {
                    LOG_E("m5311_mqtt_open error,rc:%d\r\n", rc);
                }
                break;

            case M5311_MQTT_STAT_CONNECTED:
                exit = plum_true;
                break;

            case M5311_MQTT_STAT_CONNECTING:
            case M5311_MQTT_STAT_RECONNECTING:
                break;
        }
    }

    return (rc);
}

PLUM_PRIVATE
PLUM_INLINE plum_s32 mqtt_suscribe(plum_s8 *topic)
{
    return m5311_mqtt_sub(topic);
}

PLUM_PRIVATE
PLUM_INLINE plum_s32 mqtt_publish(plum_s8 *topic, plum_s8 *msg)
{
    return mqtt_m5311_pub(topic, msg, strlen(msg));
}

PLUM_PRIVATE
plum_void mqtt_loop(plum_void)
{
    char act[DEV_M5311_UART_RX_MAX] = {0};
    int  rc = plum_hal_uart_recv(DEV_M5311_UART, act, sizeof(act),
                                DEV_M5311_UART_TIMEOUT);
    LOG_D("%s RX: %s", DEV_MODULE_NAME, act);
    if (rc > 0) {
        char *ptr = strstr(act, "+MQTTPUBLISH:");
        if (ptr) {
            plum_u8  cnt        = 1;
            plum_u8 *payload    = plum_null;
            plum_u32 payloadlen = 0;

            ptr = strtok(ptr, ",");
            while (ptr) {
                ptr = strtok(NULL, ",");
                switch (cnt) {
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                        break;
                    case 5:
                        payloadlen = strtoul(ptr, NULL, 10);
                        break;
                    case 6:
                        payload = ptr;
                        break;
                    default:
                        break;
                }

                cnt++;
            }
        }
    }
}

PLUM_PRIVATE
plum_s32 dev_m5311_init(plum_device_ref_t dev)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        m5311_power_on();
        LOG_D("%s power On", DEV_MODULE_NAME);

    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 dev_m5311_fini(plum_device_ref_t dev)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        m5311_power_off();
        LOG_D("%s power Off", DEV_MODULE_NAME);

    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 dev_m5311_reset(plum_device_ref_t dev)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        m5311_power_reset();
        LOG_D("%s Reset", DEV_MODULE_NAME);

    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_s32 dev_m5311_setpm(plum_device_ref_t dev, plum_dev_pm_t pm)
{
}

PLUM_PRIVATE
plum_s32 dev_m5311_read(plum_device_ref_t dev, plum_dev_read_t *arg)
{
}

PLUM_PRIVATE
plum_s32 dev_m5311_write(plum_device_ref_t dev, plum_dev_write_t *arg)
{
}

PLUM_PRIVATE
plum_s32 dev_m5311_ioctl(plum_device_ref_t dev, plum_u32 ctrl_id,
                         plum_void *arg)
{
    plum_s32 rc = PLUM_ECODE_OK;

    switch (ctrl_id) {
        case PLUM_MQTT_IOCTL_CON:
            break;

        case PLUM_MQTT_IOCTL_DISCON:
            break;

        case PLUM_MQTT_IOCTL_PUB:
            break;

        case PLUM_MQTT_IOCTL_SUB:
            break;

        case PLUM_MQTT_IOCTL_UNSUB:
            break;

        case PLUM_MQTT_IOCTL_MSG_CB:
            break;

        case PLUM_MQTT_IOCTL_YIELD:
            break;

        default:
            break;
    }

    return (rc);
}

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PRIVATE plum_dev_api_t m5311_api = {.init   = dev_m5311_init,
                                         .fini   = dev_m5311_fini,
                                         .reset  = dev_m5311_reset,
                                         .setpm  = dev_m5311_setpm,
                                         .read   = dev_m5311_read,
                                         .write  = dev_m5311_write,
                                         .ioctrl = dev_m5311_ioctl};

PLUM_DEVICE_EXPORT(onemo_m5311, &m5311_api);

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
